﻿using System;
using System.Collections.Generic;
using System.Linq;
using HappyBa.Core.Interfaces;

namespace HappyBa.Core.Data
{
    public class Metrics : IMetrics
    {
        public ReceivedData Data { get; private set; }
        public UIProject[] Projects { get; private set; }
        public IList<UIAssignment> Assignables { get; private set; }
        public ISet<string> Roles { get; private set; }
        public ISet<string> Types { get; private set; }
        public ISet<string> States { get; private set; }
        public IList<User> Users { get; private set; }
        public IList<User> AllUsers { get; private set; }
        public IDictionary<int, string> UserRoleAssociations { get; private set; }
        public IDictionary<UIProject, UIBug[]> BugsByRelease { get; private set; }
        public IList<UIEstimatableEntityBase> EstimatableEntities { get; private set; }
        public IList<UIUserStory> UserStories { get; private set; }
        public IList<UITask> Tasks { get; private set; }
        public IList<UIBug> Bugs { get; private set; }
        public IList<UIImpediment> Impediments { get; private set; }
        public IDictionary<User, Time[]> UserTimes { get; private set; }

        public bool IsFinished(UIStatebleEntityBase entity)
        {
            var state = entity.EntityState.Name;
            return string.Equals(state, "Done") ||
                string.Equals(state, "Ready for Deployment to Regression") ||
                string.Equals(state, "Ready for Post Card Huddle") ||
                string.Equals(state, "In QA") ||
                string.Equals(state, "Ready for QA") ||
                string.Equals(state, "Resolved") ||
                string.Equals(state, "Ready to Deploy to QA");
        }

        public bool IsReadyForDevelopment(UIStatebleEntityBase entity)
        {
            var state = entity.EntityState.Name;
            return string.Equals(state, "Open") ||
                string.Equals(state, "Analysis in Progress") ||
                string.Equals(state, "Ready for Development") ||
                string.Equals(state, "Backlog");
        }

        public bool IsInProgress(UIStatebleEntityBase entity)
        {
            var state = entity.EntityState.Name;
            return string.Equals(state, "In Development") ||
                string.Equals(state, "Rework");
        }

        public bool IsResolved(UIStatebleEntityBase entity)
        {
            var state = entity.EntityState.Name;
            return string.Equals(state, "Resolved");
        }

        public bool IsFailed(UIStatebleEntityBase entity)
        {
            return entity.EntityState.Name.IndexOf("Fail", StringComparison.OrdinalIgnoreCase) != -1;
        }

        public bool IsDeveloper(User user)
        {
            var role = UserRoleAssociations[user.Id];
            return string.Equals(role, "Developer", StringComparison.OrdinalIgnoreCase);
        }

        public bool IsUserStory(UIStatebleEntityBase entity)
        {
            return entity is UIUserStory;
        }

        public bool IsBug(UIStatebleEntityBase entity)
        {
            return entity is UIBug;
        }

        public bool IsTask(UIStatebleEntityBase entity)
        {
            return entity is UITask;
        }

        public Metrics(ReceivedData data, MetricsSearch search = null)
        {
            Data = data;
            BuildTree(search);
        }

        void BuildTree(MetricsSearch search)
        {
            Projects = Data.Projects
                .Select(x => new UIProject(x, Data))
                .ToArray();
            if (search != null)
            {
                Projects = Projects
                    .Select(x => Filter(x, search))
                    .ToArray();
            }
            EstimatableEntities = Projects.SelectMany(
                    p => p.Bugs
                        .Concat(p.UserStories.SelectMany(
                            u=> new UIEstimatableEntityBase[] { u }
                            .Concat(u.Bugs.Cast<UIEstimatableEntityBase>()
                            .Concat(u.Tasks)
                            )
                        ))
                    ).ToArray();
            UserStories = EstimatableEntities.Where(x => string.Equals(x.EntityType.Name, "UserStory")).Cast<UIUserStory>().ToArray();
            Tasks = EstimatableEntities.Where(x => string.Equals(x.EntityType.Name, "Task")).Cast<UITask>().ToArray();
            Bugs = EstimatableEntities.Where(x => string.Equals(x.EntityType.Name, "Bug")).Cast<UIBug>().ToArray();
            Assignables = EstimatableEntities.SelectMany(x=>x.Assignments).ToArray();
            Impediments = EstimatableEntities.SelectMany(x => x.Impediments).ToArray();
            var usersData = Assignables
                    .GroupBy(x=>x.GeneralUser.Id)
                    .ToDictionary(x=>x.First().GeneralUser, x=>x.First().Role.Name );
            if (search != null)
            {
                usersData = usersData.Where(x => search.Users.Contains(x.Key.Id))
                    .ToDictionary(x => x.Key, x => x.Value);
                Impediments = Impediments.Where(x=>CanInclude(x,search)).ToArray();
            }
            UserRoleAssociations = usersData.ToDictionary(x => x.Key.Id, x => x.Value);
            Users = usersData.Select(x => x.Key).OrderBy(x => x.FirstName).ToArray();
            AllUsers = Data.AllUsers.OrderBy(x => x.Email).ToArray();
            Roles = new SortedSet<string>(usersData.Select(x => x.Value));
            Types = new SortedSet<string>(EstimatableEntities.Select(x => x.EntityType.Name));
            States = new SortedSet<string>(EstimatableEntities.Cast<UIStatebleEntityBase>().Concat(Impediments).Select(x => x.EntityState.Name));
            BugsByRelease = Projects.ToDictionary(x => x, x => x.Bugs.Concat(x.UserStories.SelectMany(z => z.Bugs)).ToArray());
            UserTimes = Users.ToDictionary(x => x, x => Data.Times.Where(y => y.User.Id == x.Id).ToArray());
        }

        private UIProject Filter(UIProject project, MetricsSearch search)
        {
            project.Bugs = project.Bugs.Where(x => CanInclude(x, search)).ToArray();
            project.UserStories = project.UserStories.Where(x => CanInclude(x, search)).ToArray();
            foreach (var story in project.UserStories)
            {
                story.Impediments = story.Impediments.Where(x => CanInclude(x, search)).ToArray();
                story.Bugs = story.Bugs.Where(x => CanInclude(x, search)).ToArray();
                story.Tasks = story.Tasks.Where(x => CanInclude(x, search)).ToArray();
            }
            project.UserStories = project.UserStories.Where(x => CanInclude(x, search)).ToArray();
            return project;
        }

        private bool CanInclude(UIStatebleEntityBase item, MetricsSearch search)
        {
            if (search.CustomSearch!=null)
            {
                if (!search.CustomSearch(item)) 
                    return false;
            }
            if (item is UIUserStory) return true;
            var sItem = item as UIStatebleEntityBase;
            if (sItem != null )
            {
                if (!search.States.Any(x => string.Equals(x, sItem.EntityState.Name)))
                    return false;
                var eItem = sItem as UIEstimatableEntityBase;
                if (eItem !=null )
                {
                    if (!eItem.Assignments.Any()) return true;
                    if (!search.Users.Any(x => eItem.Assignments.Any(y => x == y.GeneralUser.Id)))
                        return false;
                    if (!search.Roles.Any(x => eItem.Assignments.Any(y => string.Equals(x, y.Role.Name, StringComparison.OrdinalIgnoreCase))))
                        return false;
                }
            }
            return true;
        }
    }
}
